home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 2 / MacMania 2.toast / Demo's / Tools&Utilities / Programming / MacStarter Pascal 1.0 / xWindows definition files / xTextWindow.p < prev    next >
Encoding:
Text File  |  1993-08-07  |  27.0 KB  |  915 lines  |  [TEXT/PJMM]

  1. unit xTextWindow;
  2.  
  3. { This unit provides support for a text  editing window, similar to that used in }
  4. { TeachText.  The window can support only a limited number of characters.  The }
  5. { absolute limit is about 32000, but performance degrades long before that. }
  6. { The window will have one vertical scroll bar, which is used to scroll the text. }
  7. { All mouse and editing operations are supported.  A procedure twUpdateEditMenu }
  8. { is exported which can be used to enable or disable the entries in the Edit Menu }
  9. { according to the state of the front window.  The text in the window can be "locked"; }
  10. { If it is, no editing operations are allowed. }
  11. {     You can leave space at the top and at the right of the edit text.  This space could }
  12. { be used for xWindowDecorations.   (However, note that an xTextWindow will be }
  13. { compatible with an xWindowDecoration of class xStringInput only if the edit text }
  14. { is locked; otherwise, all keystrokes are sent to the edit text, and the xStringInput }
  15. { can't be used.) }
  16.  
  17. interface
  18.  
  19. uses
  20.     xWindow;
  21.  
  22.  
  23. type
  24.  
  25.     xTextWindow = object(xWindow)   { a window for displaying and/or editing text }
  26.  
  27.             TE: TEHandle;  { handle to a standard Mac TEdit data structure }
  28.             linesInTE: integer;   { number of lines currently in edit text }
  29.             linesPerPage: integer;  { number of lines to scroll for a click in the gray }
  30.                                             { area of the scroll bar }
  31.             topLeftChar: integer;  { the number of the first character currently on screen }
  32.             topTextOffset: integer;  { space left in window above edit text }
  33.             leftTextOffset: integer;  { space left in window to the left of window }
  34.             maxChars: integer;  { maximum characters allowed in text before it is "trimmed"  }
  35.                                        { if the number of characters in the window exceed this limit by }
  36.                                        { 2000 characters, lines are trimmed from the beginning of the }
  37.                                        { text to bring the number of characters below maxChars.  The }
  38.                                        { value for maxChars is forced into the range 3000 .. 30000. }
  39.             trimmed: boolean;   { this is set to false at the same times changed is set to false, }
  40.                                        { and is set to true when the window is trimmed. }
  41.             locked: boolean;   { set to true when the text is locked  }
  42.             changed: boolean;  { set to true whenever the text is modified }
  43.             cursorHandler: xWindowDecoration;  { used to display an iBeamCursor when }
  44.                                                                    { the cursor is over the edit text. }
  45.             tabCt: integer;   { tabs are set every tabCt spaces; the default is 1, so that}
  46.                                       { ordinarily, tabs act just like spaces }
  47.  
  48.             procedure SetDefaults;
  49.             override;
  50.            { sets up the widow to have a vertical scroll bar but no horizontal scroll bar, }
  51.            { with no extra space at the top or left of the text.  Also sets maxChars = 10000 }
  52.            { and locked = false. }
  53.            { NOTE:  If you change the window to have a horizontal scroll bar, then the window }
  54.            {            will not use autowrap; user must press return to start a new line. }
  55.             procedure xTextWindow.openInRect (title: string;
  56.                                         left, top, right, bottom: integer);
  57.             override;
  58.            { open a text window in the given rectangle; you can also simply use }
  59.            { open(title).  these procedures have the same meaning as for xWindow. }
  60.             procedure clearAllText;
  61.            { delete all the text in the window, leaving the window empty }
  62.             procedure installText (theText: CharsHandle);
  63.            { throw away any current text in the window and replace it with theText }
  64.             function getText: CharsHandle;
  65.            { return a handle to the text in the window; this is a handle to the actual }
  66.            { text being used in the TEdit, NOT A COPY }
  67.             procedure InsertString (str: string);
  68.            { add the given string to the window at the current insertion point, or at the }
  69.            { start of the currently hilited range if ther is one; the window is scrolled if }
  70.            { necessary so that at least the start of the inserted string is visible.  Note that }
  71.            { this might cause "trimming" of the text if it grows beyond the maximum length. }
  72.             procedure appendString (str: string);
  73.            { adds str to the end of the text by moving the insertion point to the end }
  74.            { of the text, then calling InsertString; scrolling and trimming may occur }
  75.             procedure appendCR;
  76.             { adds a carriage return to the end of the text (by calling }
  77.             { appendString(chr(13))); scrolling and trimming may occur. }
  78.  
  79.             procedure lock;
  80.             { after this is called, the user cannot edit the text; clicking, keystrokes and }
  81.             { Edit menu commands are disabled, and no blinking insertion point is shown. }
  82.             { If you want a window that simply displayed text that the user can read, call }
  83.             { this immediately after opening the window.  (Note that YOU can still change }
  84.             { the text with the procedures listed above. }
  85.             procedure unlock;
  86.             { Remove the lock on user editing of the text. }
  87.  
  88.             procedure declareClean;
  89.             function dirty: boolean;
  90.             { These are provided mostly when you are working with files.  "Dirty" }
  91.             { will return true whenever the contents of the window need to be saved }
  92.             { because they have beenn changed in some way.  When you save the contents, }
  93.             { you should call declareClean.  This is called automatically when you call }
  94.             { clearText (probably in response to a New command), or installText }
  95.             { (probably when you load a file.) }
  96.  
  97.             procedure setFont (fontNumber, pointSize: integer);
  98.             { When you open a window, it is set to display its text in a default style }
  99.             { (the application font, probably Geneva) and size (12 point). }
  100.             { You can change the font and size, but only for all the text at once, with }
  101.             { this procedure.  The TEdit used in this record does not support mixed }
  102.             { fonts or sizes. The font number will probably be one of the predefined }
  103.             { font names, such as Geneva, NewYork, Monaco, Venice, SystemFont. }
  104.  
  105.             procedure scrollToSelection;
  106.             { Scroll the screen if necessary so that the start of the selection range, }
  107.             { or the insertion point, is visible }
  108.             procedure scrollTo (position: integer);
  109.             { scroll, if necessary,  so that character in specified position is visible }
  110.             procedure setSelectionRange (start, finish: integer);
  111.             { set range of hilited text (used, for example in Cut and Copy commands) }
  112.             function selectStart: integer;
  113.             { starting position of hilited text }
  114.             function selectEnd: integer;
  115.             { ending position of hilited text; if selectStart and selectEnd are equal, then }
  116.             { there is an insertion point rather than a hilited range }
  117.             procedure doTab (TE: TEHandle);
  118.             { for responding to a tab character }
  119.  
  120.             procedure doClear;
  121.             procedure doCut;
  122.             procedure doCopy;
  123.             procedure doPaste;
  124.             procedure doEditMenu (itemNum: integer);
  125.              { These respond to edit menmu commands;  doEditMenu simply routes }
  126.              { one of the four preceding procedures }
  127.  
  128.   { OVERRIDEN METHODS: }
  129.  
  130.             procedure doContentClick (localPt: point;
  131.                                         modifiers: longint);
  132.             override;
  133.              { respond to a user click on the text;  this can move insertion point or }
  134.              { set the selection range if the user drags the mouse }
  135.             procedure doKey (ch: char;
  136.                                         modifiers: longint);  { respond to a user keystroke }
  137.             override;
  138.             procedure doRedraw (badRect: rect);
  139.             override;
  140.             procedure AdjustToNewSize;
  141.             override;
  142.             procedure doVScroll (dv: integer);  { scrolls the text by dv lines }
  143.             override;
  144.             procedure doHScroll (dh: integer);
  145.             override;
  146.             procedure doActivate (active: boolean);
  147.             override;
  148.             procedure doClose;  { disposes of TE then closes window }
  149.             override;
  150.             procedure idle;  { calles TEIdle, if the text is not locked, to blink the insertion point }
  151.             override;
  152.  
  153.         end;
  154.  
  155.  
  156. procedure twUpdateEditMenu (editMH: menuHandle);
  157.    { Checks if the front window is an xTextWindow; if no, it appropriately enables }
  158.    { and disables the Edit Menu commands; if not, all Edit Menu commands are }
  159.    { disabled.   This is provided as a convenience. }
  160.  
  161.  
  162. implementation
  163.  
  164.  
  165. var
  166.     clickedWin: xTextWindow;  { for use in ClickLoop procedure }
  167.     clickSaveClip: RgnHandle;
  168.  
  169. function ClickLoopProc: boolean;
  170.     var
  171.         newTopLine: integer;
  172.         topLine: integer;
  173.         savePort: GrafPtr;
  174.         pt: point;
  175.         dh: integer;
  176.     begin
  177.         GetPort(savePort);
  178.         with clickedWin do begin { clickedWin must be set by doContentClick }
  179.                 SetPort(theWindow);
  180.                 GetMouse(pt);
  181.                 topLine := GetVVal;
  182.                 if (pt.v < TE^^.viewRect.top - 4) & (topLine > 0) then begin
  183.                         newTopLine := topLine - 1;
  184.                         if TE^^.SelStart <= TE^^.lineStarts[topLine] then
  185.                             TE^^.selStart := TE^^.lineStarts[newTopLine];
  186.                     end
  187.                 else if (pt.v > TE^^.viewRect.bottom + 4) & (topLine < linesInTE - linesPerPage) then begin
  188.                         newTopLine := topLine + 1;
  189.                         if TE^^.selEnd >= TE^^.lineStarts[TopLine + linesPerPage] then
  190.                             if newTopLine = linesInTE - linesPerPage then
  191.                                 TE^^.selEnd := TE^^.TELength + 1
  192.                             else
  193.                                 TE^^.selEnd := TE^^.lineStarts[newTopLine + linesPerPage]
  194.                     end
  195.                 else begin
  196.                         newTopLine := topLine
  197.                     end;
  198.                 if not (hasHScroll in features) then
  199.                     dh := 0
  200.                 else if pt.h < TE^^.viewRect.left - 4 then begin
  201.                         dh := pt.h - (TE^^.viewRect.left - 4);
  202.                         if dh < -12 then
  203.                             dh := -12;
  204.                         if GetHVal + dh < 0 then
  205.                             dh := -GetHVal;
  206.                     end
  207.                 else if pt.h > TE^^.viewRect.right + 4 then begin
  208.                         dh := pt.h - (TE^^.viewRect.right + 4);
  209.                         if dh > 12 then
  210.                             dh := 12;
  211.                         if GetHVal + dh > GetHMax then
  212.                             dh := GetHmax - GetHVal;
  213.                     end
  214.                 else
  215.                     dh := 0;
  216.                 if (dh <> 0) | (topLine <> newTopLine) then begin
  217.                         GetClip(clickSaveClip); { clickSaveClip must be newed by doContentClick }
  218.                         ClipRect(theWindow^.portRect);
  219.                         if topLine <> newTopLine then begin
  220.                                 SetVVal(newTopLine);
  221.                                 doVScroll(newTopLine - topLine)
  222.                             end;
  223.                         if dh <> 0 then begin
  224.                                 SetHVal(GetHVal + dh);
  225.                                 doHScroll(dh);
  226.                             end;
  227.                         SetClip(clickSaveClip);
  228.                     end;
  229.             end;
  230.         SetPort(savePort);
  231.         ClickLoopProc := true;
  232.     end;
  233.  
  234.  
  235. procedure xTextWindow.setDefaults;
  236.     begin
  237.         inherited setDefaults;
  238.         setFeatures([hasGoAway, hasGrow, hasZoom, hasVScroll]);
  239.         topTextOffset := 0;
  240.         leftTextOffset := 0;
  241.         maxChars := 10000;
  242.         locked := false;
  243.         tabCt := 0;
  244.     end;
  245.  
  246.  
  247. procedure xTextWindow.openInRect (title: string;
  248.                                 left, top, right, bottom: integer);
  249.     var
  250.         R: rect;
  251.         savePort: GrafPtr;
  252.         ch: cursHandle;
  253.         width, height: integer;
  254.         info: FontInfo;
  255.         pointSize, fontNumber: integer;
  256.     begin
  257.         setDefaults;
  258.         if minSize.v < 65 then
  259.             minSize.v := 65;
  260.         inherited doBasicOpen(title, left, top, right, bottom);
  261.         GetPort(savePort);
  262.         SetPort(theWindow);
  263.         R := theWindow^.portRect;
  264.         R.right := R.right - 15;
  265.         R.left := R.left + leftTextOffset;
  266.         R.top := R.top + topTextOffset;
  267.         if hasHScroll in features then
  268.             R.bottom := R.bottom - 15;
  269.         InsetRect(R, 4, 4);
  270.         if EmptyRect(R) then
  271.             R := theWindow^.portRect;
  272.         TE := TENew(R, R);
  273.         if realFont(geneva, 12) then
  274.             TextFont(geneva)
  275.         else if realFont(monaco, 12) then
  276.             TextFont(monaco)
  277.         else
  278.             TextFont(systemFont);
  279.         GetFontInfo(info);
  280.         TE^^.lineHeight := info.ascent + info.descent + info.leading;
  281.         TE^^.fontAscent := info.ascent;
  282.         TE^^.txFont := theWindow^.txFont;
  283.         TE^^.txSize := theWindow^.txSize;
  284.         if hasHScroll in Features then
  285.             maxSize.h := 150 * CharWidth('0') + 1;
  286.         hpixelsperline := 12;
  287.         SetPort(savePort);
  288.         linesPerPage := (R.bottom - R.top + 4) div TE^^.lineHeight;
  289.         R.bottom := R.top + TE^^.lineHeight * linesPerPage;
  290.         TE^^.viewRect := R;
  291.         if hasHScroll in features then begin
  292.                 TE^^.crOnly := -1;
  293.                 SetHMax(maxSize.h - (theWindow^.portRect.right - theWindow^.portRect.left) - 1);
  294.             end;
  295.         SetLinesPerPage(4 * (theWindow^.portRect.right - theWindow^.portRect.left) div 5, linesPerPage);
  296.         SetClikLoop(@ClickLoopProc, TE);
  297.         linesInTE := 0;
  298.         changed := false;
  299.         trimmed := false;
  300.         topLeftChar := 0;
  301.         new(cursorHandler);
  302.         cursorHandler.init;
  303.         height := TE^^.viewRect.bottom - TE^^.viewRect.top;
  304.         width := TE^^.viewRect.right - TE^^.viewRect.left;
  305.         cursorHandler.install(self, leftTextOffset + 4, topTextOffset + 4, width, height);
  306.         ch := GetCursor(iBeamCursor);
  307.         if ch <> nil then
  308.             cursorHandler.useCursor(ch^^);
  309.         if locked then begin
  310.                 TEDeactivate(TE);
  311.                 cursorHandler.grayedOut := true;
  312.             end;
  313.     end;
  314.  
  315. function CountTELines (TE: TEHandle): integer;
  316.     var
  317.         ct: integer;
  318.         chars: CharsHandle;
  319.         lastCh: char;
  320.     begin
  321.         ct := TE^^.nLines;
  322.         if ct > 0 then begin
  323.                 chars := TEGetText(TE);
  324.                 lastCh := Chars^^[TE^^.teLength - 1];
  325.                 if lastCh = chr(13) then
  326.                     ct := ct + 1;
  327.             end;
  328.         CountTELines := ct;
  329.     end;
  330.  
  331. procedure CheckScroll (win: xTextWindow);
  332.     var
  333.         topLine, newTopLine: integer;
  334.     begin
  335.         with win do begin
  336.                 TopLine := GetVVal;
  337.                 newTopLine := topLine;
  338.                 if linesInTE <> CountTElines(TE) then begin
  339.                         linesInTE := CountTELines(TE);
  340.                         if linesInTE - linesPerPage > 0 then begin
  341.                                 SetVMax(linesInTE - linesPerPage);
  342.                                 if newTopLine > linesInTE - linesPerPage then
  343.                                     newTopLine := linesInTE - linesPerPage
  344.                             end
  345.                         else begin
  346.                                 SetVMax(0);
  347.                                 newTopLine := 0;
  348.                             end;
  349.                     end;
  350.                 if TE^^.selStart < TE^^.lineStarts[newTopLine] then begin
  351.                         repeat
  352.                             newTopLine := newTopLine - 1
  353.                         until (newTopLine = 0) | (TE^^.selStart >= TE^^.lineStarts[newTopLine])
  354.                     end
  355.                 else if (newTopLine + linesPerPage < linesInTE) & (TE^^.selEnd >= TE^^.lineStarts[newTopLine + linesPerPage]) then begin
  356.                         repeat
  357.                             newTopLine := newTopLine + 1
  358.                         until (newTopLine + linesPerPage = linesInTE) | (TE^^.selEnd < TE^^.lineStarts[newTopLine + linesPerPage]);
  359.                     end;
  360.                 if (newTopLine <> topLine) then begin
  361.                         SetVVal(newTopLine);
  362.                         doVScroll(newTopLine - topLine);
  363.                     end;
  364.             end;
  365.         win.ScrollToSelection;
  366.     end;
  367.  
  368. procedure CheckTrim (win: xTextWindow);
  369.     var
  370.         line: integer;
  371.         trimAmount: integer;
  372.         pos: integer;
  373.         saveSelStart, saveSelEnd: integer;
  374.         chars: CharsHandle;
  375.         i: integer;
  376.     begin
  377.         if win.maxChars > 30000 then
  378.             win.maxChars := 30000
  379.         else if win.maxChars < 3000 then
  380.             win.maxChars := 3000;
  381.         if win.TE^^.teLength > win.maxChars + 2000 then begin
  382.                 trimAmount := win.TE^^.teLength - win.maxChars;
  383.                 line := 1;
  384.                 while (line < win.TE^^.nLines) & (win.TE^^.lineStarts[line] < trimAmount) do
  385.                     line := line + 1;
  386.                 if line < win.TE^^.nLines then begin
  387.                         pos := win.TE^^.lineStarts[line] - 1;
  388.                         saveSelStart := win.TE^^.selStart - pos;
  389.                         if saveSelStart < 0 then
  390.                             saveSelStart := 0;
  391.                         saveSelEnd := win.TE^^.selEnd - pos;
  392.                         if saveSelEnd < 0 then
  393.                             saveSelEnd := 0;
  394.                         chars := TEGetText(win.TE);
  395.                         for i := pos to win.TE^^.teLength - 1 do
  396.                             chars^^[i - pos] := chars^^[i];
  397.                         SetHandleSize(Handle(chars), win.TE^^.teLength - pos);
  398.                         win.TE^^.teLength := win.TE^^.teLength - pos;
  399.                         TECalText(win.TE);
  400.                         TESetSelect(saveSelStart, saveSelEnd, win.TE);
  401.                         CheckScroll(win);
  402.                         win.trimmed := true;
  403.                     end;
  404.             end;
  405.     end;
  406.  
  407.  
  408. procedure xTextWindow.clearAllText;
  409.     var
  410.         R: Rect;
  411.         savePort: GrafPtr;
  412.     begin
  413.         TESetSelect(0, 32000, TE);
  414.         TEDelete(TE);
  415.         R := theWindow^.portRect;
  416.         R.right := R.right - 15;
  417.         R.top := R.top + topTextOffset;
  418.         R.left := R.left + leftTextOffset;
  419.         R.bottom := R.bottom - 15;
  420.         GetPort(savePort);
  421.         SetPort(theWindow);
  422.         InvalRect(R);
  423.         SetPort(savePort);
  424.         InsetRect(R, 4, 4);
  425.         linesPerPage := (R.bottom - R.top + 4) div TE^^.lineHeight;
  426.         R.bottom := R.top + TE^^.lineHeight * linesPerPage;
  427.         vLinesPerPage := linesPerPage;
  428.         TE^^.destRect := R;
  429.         TE^^.viewRect := R;
  430.         setVMax(0);
  431.         setVVal(0);
  432.         linesInTE := 0;
  433.         topLeftChar := 0;
  434.         changed := false;
  435.         trimmed := false;
  436.     end;
  437.  
  438. procedure xTextWindow.installText (theText: CharsHandle);
  439.     begin
  440.         if GetHandleSize(handle(theText)) >= maxChars + 2000 then
  441.             EXIT(installText);
  442.         clearAllText;
  443.         TE^^.hText := Handle(theText);
  444.         TECalText(TE);
  445.         linesInTE := CountTELines(TE);
  446.         if linesInTE > linesPerPage then
  447.             SetVMax(linesInTE - linesPerPage)
  448.         else
  449.             SetVMax(0);
  450.         changed := false;
  451.         trimmed := false;
  452.     end;
  453.  
  454. function xTextWindow.getText: CharsHandle;
  455.     begin
  456.         getText := TEGetText(TE);
  457.     end;
  458.  
  459. procedure xTextWindow.InsertString (str: string);
  460.     begin
  461.         if str <> '' then begin
  462.                 TEInsert(@str[1], length(str), TE);
  463.                 changed := true;
  464.                 CheckScroll(self);
  465.                 CheckTrim(self);
  466.             end;
  467.     end;
  468.  
  469. procedure xTextWindow.appendString (str: string);
  470.     begin
  471.         setSelectionRange(maxint, maxint);
  472.         insertString(str);
  473.     end;
  474.  
  475. procedure xTextWindow.appendCR;
  476.     begin
  477.         appendString(chr(13));
  478.     end;
  479.  
  480. function xTextWindow.dirty: boolean;
  481.     begin
  482.         dirty := changed
  483.     end;
  484.  
  485. procedure xTextWindow.declareClean;
  486.     begin
  487.         changed := false;
  488.         trimmed := false;
  489.     end;
  490.  
  491. procedure xTextWindow.setFont (fontNumber, pointSize: integer);
  492.     var
  493.         savePort: GrafPtr;
  494.         info: FontInfo;
  495.         oldFont, oldSize: integer;
  496.         line: integer;
  497.     begin
  498.         GetPort(savePort);
  499.         SetPort(theWindow);
  500.         oldFont := theWindow^.txFont;
  501.         oldSize := theWindow^.txSize;
  502.         TextFont(fontNumber);
  503.         TextSize(pointSize);
  504.         GetFontInfo(info);
  505.         TE^^.lineHeight := info.ascent + info.descent + info.leading;
  506.         TE^^.fontAscent := info.ascent;
  507.         TE^^.txFont := theWindow^.txFont;
  508.         TE^^.txSize := theWindow^.txSize;
  509.         TextFont(oldFont);
  510.         TextSize(oldSize);
  511.         AdjustToNewSize;
  512.         InvalRect(TE^^.viewRect);
  513.         SetPort(savePort);
  514.     end;
  515.  
  516. procedure xTextWindow.scrollToSelection;
  517.     begin
  518.         scrollTo(TE^^.selStart);
  519.     end;
  520.  
  521. procedure xTextWindow.scrollTo (position: integer);
  522.     var
  523.         topLine: integer;
  524.         n: integer;
  525.         line: integer;
  526.         w, dh: integer;
  527.         SavePort: GrafPtr;
  528.         hText: CharsHandle;
  529.         oldFont: integer;
  530.         oldSize: integer;
  531.     begin
  532.         topLine := GetVVal;
  533.         if (position < TE^^.lineStarts[topLine]) then begin
  534.                 n := topLine;
  535.                 repeat
  536.                     n := n - 1
  537.                 until (n = 0) | (position >= TE^^.lineStarts[n]);
  538.                 line := n;
  539.                 SetVVal(n);
  540.                 doVScroll(n - topLine);
  541.             end
  542.         else begin
  543.                 n := topLine;
  544.                 repeat
  545.                     n := n + 1
  546.                 until (n >= linesInTE) | (position < TE^^.lineStarts[n]);
  547.                 line := n - 1;
  548.                 if (topLine + linesPerPage < linesInTE) & (position >= TE^^.lineStarts[topLine + linesPerPage]) then begin
  549.                         n := n - linesPerPage;
  550.                         SetVVal(n);
  551.                         doVScroll(n - topLine);
  552.                     end
  553.             end;
  554.         GetPort(SavePort);
  555.         SetPort(theWindow);
  556.         oldFont := theWindow^.txFont;
  557.         oldSize := theWindow^.txSize;
  558.         TextFont(TE^^.txFont);
  559.         TextSize(TE^^.txSize);
  560.         w := 0;
  561.         n := TE^^.lineStarts[line];
  562. {$ifc option(debug)}
  563.         if n > position then begin
  564.                 TellUser('Bad logic detected in procedure ScrollTo.  Program is being terminated');
  565.                 halt;
  566.             end;
  567. {$endc}
  568.         hText := GetText;
  569.         while n < position do begin
  570.                 w := w + CharWidth(hText^^[n]);
  571.                 n := n + 1;
  572. {$ifc option(debug)}
  573.                 if ((line + 1 < linesInTE) & (n >= TE^^.lineStarts[line + 1])) | (n > TE^^.teLength) then begin
  574.                         TellUser('Bad logic detected in procedure ScrollTo.  Program is being terminated');
  575.                         halt;
  576.                     end
  577. {$endc}
  578.             end;
  579.         if hasHScroll in features then begin
  580.                 if w + TE^^.destRect.left < TE^^.viewRect.left + 24 then begin
  581.                         dh := w + TE^^.destRect.left - (TE^^.viewRect.left + 24);
  582.                         if GetHVal + dh < 0 then
  583.                             dh := -GetHVal;
  584.                     end
  585.                 else if w + TE^^.destRect.left >= TE^^.viewRect.right then begin
  586.                         dh := w + TE^^.destRect.left - (TE^^.viewRect.right - 1);
  587.                         if GetHVal + dh > GetHMax then
  588.                             dh := GetHMax - GetHVal;
  589.                     end
  590.                 else
  591.                     dh := 0;
  592.                 if dh <> 0 then begin
  593.                         SetHVal(GetHVal + dh);
  594.                         doHScroll(dh);
  595.                     end;
  596.             end;
  597.         TextFont(oldFont);
  598.         TextSize(oldSize);
  599.         SetPort(SavePort);
  600.     end;
  601.  
  602. procedure xTextWindow.setSelectionRange (start, finish: integer);
  603.     begin
  604.         TESetSelect(start, finish, TE);
  605.     end;
  606.  
  607. function xTextWindow.selectStart: integer;
  608.     begin
  609.         selectStart := TE^^.selStart;
  610.     end;
  611.  
  612. function xTextWindow.selectEnd: integer;
  613.     begin
  614.         selectEnd := TE^^.selEnd;
  615.     end;
  616.  
  617. procedure twUpdateEditMenu (editMH: menuHandle);
  618.     var
  619.         win: WindowPtr;
  620.         xWin: xWindow;
  621.         i: integer;
  622.     begin
  623.         win := FrontWindow;
  624.         if (win <> nil) & (WindowPeek(win)^.windowKind < 0) then begin { it's a desk accessory }
  625.                 EnableItem(editMH, 1);            { Enable standard items in Edit menu }
  626.                 for i := 3 to 6 do
  627.                     EnableItem(editMH, i);
  628.                 DisableItem(editMH, 8);
  629.                 DisableItem(editMH, 9);
  630.                 EXIT(twUpdateEditMenu);
  631.             end;
  632.         DisableItem(editMH, 1);
  633.         DisableItem(editMH, 3);
  634.         DisableItem(editMH, 4);
  635.         DisableItem(editMH, 5);
  636.         DisableItem(editMH, 6);
  637.         DisableItem(editMH, 8);
  638.         DisableItem(editMH, 9);
  639.         if (win <> nil) & (Window2XWindow(win, xWin)) then begin
  640.                 if member(xWin, xTextWindow) & not xTextWindow(xWin).locked then begin
  641.                         if xTextWindow(xWin).TE^^.selStart < xTextWindow(xWin).TE^^.selEnd then begin
  642.                                 EnableItem(editMH, 3);
  643.                                 EnableItem(editMH, 4);
  644.                                 EnableItem(editMH, 6);
  645.                             end;
  646.                         if TEGetScrapLen > 0 then
  647.                             EnableItem(editMH, 5);
  648.                         if xTextWindow(xWin).TE^^.teLength > 0 then begin
  649.                                 EnableItem(editMH, 8);
  650.                                 EnableItem(editMH, 9);
  651.                             end;
  652.                     end
  653.             end;
  654.     end;
  655.  
  656.  
  657. procedure xTextWindow.doClear;
  658.     begin
  659.         if not locked & (TE^^.selEnd > TE^^.selStart) then begin
  660.                 TEDelete(TE);
  661.                 changed := true;
  662.                 CheckScroll(self);
  663.             end;
  664.     end;
  665.  
  666. procedure xTextWindow.doCut;
  667.     begin
  668.         if not locked & (TE^^.selEnd > TE^^.selStart) then begin
  669.                 TECut(TE);
  670.                 changed := true;
  671.                 CheckScroll(self);
  672.             end;
  673.     end;
  674.  
  675. procedure xTextWindow.doCopy;
  676.     begin
  677.         if not locked then begin
  678.                 TECopy(TE);
  679.             end;
  680.     end;
  681.  
  682. procedure xTextWindow.doPaste;
  683.     begin
  684.         if not locked & (TEGetScrapLen > 0) then
  685.             if TEGetScrapLen + TE^^.teLength - (TE^^.selEnd - TE^^.selStart) > 32000 then
  686.                 SysBeep(5)
  687.             else begin
  688.                     TEPaste(TE);
  689.                     changed := true;
  690.                     CheckScroll(self);
  691.                     CheckTrim(self);
  692.                 end;
  693.     end;
  694.  
  695. procedure xTextWindow.doEditMenu (itemNum: integer);
  696.     begin
  697.         case itemNum of
  698.             3: 
  699.                 doCut;
  700.             4: 
  701.                 doCopy;
  702.             5: 
  703.                 doPaste;
  704.             6: 
  705.                 doClear;
  706.             8: 
  707.                 setSelectionRange(0, 32000);
  708.             9: 
  709.                 clearAllText;
  710.         end;
  711.     end;
  712.  
  713. procedure xTextWindow.doContentClick (localPt: point;
  714.                                 modifiers: longint);
  715.     var
  716.         shifted: boolean;
  717.     begin
  718.         if locked | (localPt.v < TopTextOffset) | (localPt.v > theWindow^.portrect.bottom - 15) | (localPt.h < LeftTextOffset) | (localPt.h > theWindow^.portRect.right - 15) then
  719.             inherited doContentClick(localPt, modifiers)
  720.         else begin
  721.                 clickedWin := self;
  722.                 HLock(Handle(clickedWin));
  723.                 clickSaveClip := NewRgn;
  724.                 shifted := BitAnd(modifiers, shiftKey) <> 0;
  725.                 TEClick(localPt, shifted, TE);
  726.                 DisposeRgn(clickSaveClip);
  727.                 HUnLock(Handle(clickedWin));
  728.             end;
  729.     end;
  730.  
  731. procedure xTextWindow.doTab (TE: TEHandle);
  732.     var
  733.         n: integer;
  734.         pos: integer;
  735.         i, ct, max: integer;
  736.     begin
  737.         n := 1;
  738.         pos := TE^^.selStart;
  739.         if (pos = 0) | (CharsHandle(TE^^.hText)^^[pos - 1] = chr(13)) then
  740.             ct := tabCt
  741.         else begin
  742.                 max := TE^^.nLines;
  743.                 while (n < max) & (TE^^.lineStarts[n] < pos) do
  744.                     n := n + 1;
  745.                 ct := tabCt - ((pos - TE^^.lineStarts[n - 1]) mod tabCt);
  746.             end;
  747.         for i := 1 to ct do
  748.             TEKey(' ', TE);
  749.     end;
  750.  
  751. procedure xTextWindow.doKey (ch: char;
  752.                                 modifiers: longint);
  753.     var
  754.         start, finish: integer;
  755.     begin
  756.         if locked then
  757.             inherited doKey(ch, modifiers)
  758.         else begin
  759.                 if ch = chr(3) then
  760.                     ch := chr(13);
  761.                 if (ch = chr(9)) & (tabCt > 1) then
  762.                     doTab(TE)
  763.                 else if (ch in [chr($1c)..chr($1f)]) & (TE^^.selEnd > TE^^.selStart) then begin
  764.                         start := TE^^.selStart;
  765.                         finish := TE^^.selEnd;
  766.                         if ch = chr($1e) then begin
  767.                                 SetSelectionRange(start, start);
  768.                                 TEKey(ch, TE);
  769.                             end
  770.                         else if ch = chr($1c) then begin
  771.                                 SetSelectionRange(start, start);
  772.                             end
  773.                         else if ch = chr($1f) then begin
  774.                                 SetSelectionRange(finish, finish);
  775.                                 TEKey(ch, TE)
  776.                             end
  777.                         else begin
  778.                                 SetSelectionRange(finish, finish);
  779.                             end
  780.                     end
  781.                 else
  782.                     TEKey(ch, TE);
  783.                 if not (ch in [chr($1c)..chr($1f)]) then
  784.                     changed := true;
  785.                 CheckScroll(self);
  786.                 CheckTrim(self);
  787.             end;
  788.     end;
  789.  
  790. procedure xTextWindow.doRedraw (badRect: rect);
  791.     begin
  792.         inherited doRedraw(badRect);
  793.         TEUpdate(badRect, TE);
  794.     end;
  795.  
  796. procedure xTextWindow.AdjustToNewSize;
  797.     var
  798.         R, V: Rect;
  799.         hv: integer;
  800.         line: integer;
  801.         width, height: integer;
  802.     begin
  803.         inherited AdjustToNewSize;
  804.         R := theWindow^.portRect;
  805.         R.right := R.right - 15;
  806.         R.top := R.top + topTextOffset;
  807.         R.left := R.left + leftTextOffset;
  808.         if hasHScroll in features then
  809.             R.bottom := R.bottom - 15;
  810.         InsetRect(R, 4, 4);
  811.         TE^^.destRect := R;
  812.         TE^^.viewRect := R;
  813.         linesPerPage := (R.bottom - R.top + 4) div TE^^.lineHeight;
  814.         TECalText(TE);
  815.         linesInTE := CountTELines(TE);
  816.         line := 0;
  817.         V := R;
  818.         if hasHScroll in features then begin
  819.                 hv := GetHVal;
  820.                 SetHMax(maxSize.h - (theWindow^.portRect.right - theWindow^.portRect.left) - 1);
  821.                 if hv > GetHMax then begin
  822.                         hv := GetHMax;
  823.                         SetHVal(hv);
  824.                     end;
  825.                 V.left := V.left - hv;
  826.             end;
  827.         if linesInTE - linesPerPage > 0 then begin
  828.                 while (line < TE^^.nLines) & (TE^^.lineStarts[line + 1] <= topLeftChar) do
  829.                     line := line + 1;
  830.                 if line > linesInTE - linesPerPage then
  831.                     line := linesInTE - linesPerPage;
  832.                 V.top := V.top - line * TE^^.lineHeight;
  833.                 topLeftChar := TE^^.lineStarts[line];
  834.                 SetVMax(linesInTE - linesPerPage);
  835.             end
  836.         else begin
  837.                 SetVMax(0);
  838.                 topLeftChar := 0;
  839.             end;
  840.         R.bottom := R.top + TE^^.lineHeight * linesPerPage;
  841.         TE^^.destRect := V;
  842.         TE^^.viewRect := R;
  843.         SetVVal(line);
  844.         if linesPerPage <= 3 then
  845.             SetLinesPerPage(4 * (theWindow^.portRect.right - theWindow^.portRect.left) div 5, 1)
  846.         else
  847.             SetLinesPerPage(4 * (theWindow^.portRect.right - theWindow^.portRect.left) div 5, linesPerPage - 2);
  848.         height := TE^^.viewRect.bottom - TE^^.viewRect.top;
  849.         width := TE^^.viewRect.right - TE^^.viewRect.left;
  850.         cursorHandler.move(leftTextOffset + 4, topTextOffset + 4);
  851.         cursorHandler.setSize(width, height);
  852.     end;
  853.  
  854. procedure xTextWindow.doVScroll (dv: integer);
  855.     var
  856.         newLine: integer;
  857.         topLine: integer;
  858.     begin
  859.         if dv <> 0 then begin
  860.                 TEPinScroll(0, -TE^^.lineHeight * dv, TE);
  861.                 topLine := GetVVal;
  862.                 topLeftChar := TE^^.lineStarts[topLine];
  863.             end;
  864.     end;
  865.  
  866. procedure xTextWindow.doHScroll (dh: integer);
  867.     begin
  868.         if (dh <> 0) & (hasHScroll in features) then
  869.             TEScroll(-dh, 0, TE);
  870.     end;
  871.  
  872. procedure xTextWindow.doActivate (active: boolean);
  873.     begin
  874.         inherited doActivate(active);
  875.         if not locked then
  876.             if active then
  877.                 TEActivate(TE)
  878.             else
  879.                 TEDeactivate(TE);
  880.     end;
  881.  
  882. procedure xTextWindow.lock;
  883.     begin
  884.         if not locked then begin
  885.                 TEDeactivate(TE);
  886.                 locked := true;
  887.                 cursorHandler.grayedOut := true;
  888.             end;
  889.     end;
  890.  
  891. procedure xTextWindow.unlock;
  892.     begin
  893.         if locked then begin
  894.                 if (theWindow <> nil) & (theWindow = FrontWindow) then
  895.                     TEActivate(TE);
  896.                 locked := false;
  897.                 cursorHandler.grayedOut := false;
  898.             end;
  899.     end;
  900.  
  901. procedure xTextWindow.doClose;
  902.     begin
  903.         TEDispose(TE);
  904.         inherited doClose;
  905.     end;
  906.  
  907. procedure xTextWindow.idle;
  908.     begin
  909.         if locked then
  910.             inherited idle
  911.         else
  912.             TEIdle(TE);
  913.     end;
  914.  
  915. end.